package com.sinovatech.ologger.actionlog.aop;

import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.sql.Timestamp;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

import com.sinovatech.ologger.actionlog.aop.annotation.OperationLogAnnotation;
import com.sinovatech.ologger.actionlog.aop.annotation.UnSaveLogArgsAnnotation;
import com.sinovatech.ologger.actionlog.core.LogHelper;
import com.sinovatech.ologger.actionlog.core.LogUser;
import com.sinovatech.ologger.actionlog.core.OperationLogInvocation;
import com.sinovatech.ologger.actionlog.entity.OperationLog;
import com.sinovatech.ologger.core.web.jsonplugin.JSONUtil;
import com.sinovatech.ologger.util.LogUtil;
import com.sinovatech.ologger.util.Misc;
import com.sinovatech.ologger.util.StringHelper;

public class OperationLogInterceptor implements MethodInterceptor,
		ApplicationContextAware {

	private static final Log logger = LogFactory
			.getLog(OperationLogInterceptor.class);

	private ApplicationContext context;
	private BeanContext beanContext;
	private Boolean isMustWithUser = true;// 日志记录时, 是否必须存在User

	public Object invoke(MethodInvocation invocation) throws Throwable {
		long beginTime = System.currentTimeMillis();// 日志处理时间记录 开始时间
		Object returnValue = null;
		Method method = invocation.getMethod();
		Object[] args = invocation.getArguments();
		Object target = invocation.getThis();
		boolean isExecuted = false;// 目标方法是否已执行
		try {
			if (LogHelper.isPassMethod(method)) {
				isExecuted = true;
				return invocation.proceed();// 执行目标方法
			}
			OperationLogAnnotation logAnnotation = method
					.getAnnotation(OperationLogAnnotation.class);
			OperationLogAnnotation beanAnn = target.getClass().getAnnotation(
					OperationLogAnnotation.class);
			if (beanAnn != null && !beanAnn.doLog()) {
				isExecuted = true;
				return invocation.proceed();// 执行目标方法
			}
			LogUser user = null;
			try {
				user = LogHelper.getUser();
			} catch (Exception e) {
				// user = LogHelper.getUser();
				logger.error(e.getMessage());
			}
			if ((user != null || !isMustWithUser) && logAnnotation != null
					&& logAnnotation.doLog()) {
				OperationLog log = new OperationLog();
				Timestamp curTime = Misc.getCurrentTimestamp();
				log.setCreateTime(curTime);
				log.setOperationName(logAnnotation.operationName());
				Serializable userId = user != null ? user.getUserID() : null;
				String userName = user != null ? user.getUserName() : null;
				log.setUserId(userId);
				log.setUsername(userName);
				LogHelper.getOpcMap().clear();// 清除日志操作信息
				// 取消log与permission的关联
				// log.getPermissions().addAll(
				// AuthHelper.getPermissionSetByUser(user));
				String conditionsJSON = null;
				if (logAnnotation.isRecordArgsForJSON()) {
					Annotation[][] annotations = method
							.getParameterAnnotations();
					Object[] argsTmp = getArgsTmp(args, annotations);
					conditionsJSON = JSONUtil.serialize(argsTmp, null, null,
							true, true);
					log.setConditionsJSON(conditionsJSON);
				}
				String beanName = logAnnotation.beanName();
				String creator = logAnnotation.creator();
				String methodName = logAnnotation.methodName();
				String operationDetails = logAnnotation.operationDetails();
				String checkReturnExpression = !StringHelper
						.isEmpty(logAnnotation.checkReturnExpression()) ? logAnnotation
						.checkReturnExpression()
						: beanAnn != null ? beanAnn.checkReturnExpression()
								: null;
				boolean needBeforeRun = logAnnotation.needBeforeRun();
				if (StringHelper.isEmpty(beanName) && beanAnn != null) {
					beanName = beanAnn.beanName();
					creator = beanAnn.creator();
					if (StringHelper.isEmpty(methodName)) {
						methodName = beanAnn.methodName();
					}
				}
				if (StringHelper.isEmpty(methodName)) {
					methodName = method.getName();
				}
				if (StringHelper.isEmpty(operationDetails)) {
					operationDetails = beanAnn.operationDetails();
				}
				// if (!StringHelper.isEmpty(methodName) &&
				// !StringHelper.isEmpty(creator)) {
				Object bean = beanContext.findCreatorByCreatorAndBeanName(
						creator, beanName);
				if (!creator.equals("spring")
						&& bean instanceof ApplicationContextAware) {// 如果处理类不是spring管理并且实现了ApplicationContextAware,
					// 会为其注入SpringContext
					((ApplicationContextAware) bean)
							.setApplicationContext(context);
				}
				Method logMethod = null;
				try {
					logMethod = bean.getClass().getMethod(methodName,
							OperationLogInvocation.class);
				} catch (Exception e) {
					logger.error(e.getMessage());
				}
				log.setOperationDetails(operationDetails);
				log.setSrcOperationDetails(operationDetails);
				OperationLogInvocation invocationLog = new OperationLogInvocation(
						returnValue, method, args, target, log, context);
				if (needBeforeRun) {
					invocationLog.setIsExecuted(isExecuted);
					if (logMethod != null) {
						logMethod.invoke(bean, invocationLog);
						operationDetails = log.getOperationDetails();
					}
					operationDetails = LogHelper
							.processOperationDetailsWithArgs(operationDetails,
									args);
					log.setOperationDetails(operationDetails);
				}
				long beginTimeTarget = System.currentTimeMillis();// 日志处理时间记录
				// 目标方法开始时间
				returnValue = invocation.proceed();// 执行目标方法
				long endTimeTarget = System.currentTimeMillis();// 日志处理时间记录
				// 目标方法结束时间
				isExecuted = true;
				invocationLog.setIsExecuted(isExecuted);
				invocationLog.setReturnValue(returnValue);
				Map<String, Object> opcMap = LogHelper.getOpcMap();// 去除用户存在ThreadLocal里的日志信息
				if (opcMap != null && opcMap.size() > 0) {
					log.getOpcMap().putAll(opcMap);
				}
				AfterRunLog afterRunLog = (AfterRunLog) context
						.getBean("afterRunLogThread");
				afterRunLog.init(logMethod, bean, invocationLog, log,
						operationDetails, conditionsJSON, args,
						checkReturnExpression);
				afterRunLog.start();
				// }
				long endTime = System.currentTimeMillis();// 日志处理时间记录 结束时间
				logger.info("日志拦截目标方法" + invocation.getMethod().getName());
				logger
						.info("总共耗时:"
								+ ((endTime - beginTime) / 1000)
								+ "秒 ;日志处理耗时:"
								+ (((endTime - beginTime) - (endTimeTarget - beginTimeTarget)) / 1000)
								+ "秒 ");
			}
		} catch (Exception e) {
			logger.error(LogUtil.stackTraceToString(e));
		} finally {
			if (!isExecuted) {
				isExecuted = true;
				return invocation.proceed();// 执行目标方法
			}
		}
		return returnValue;
	}

	private Object[] getArgsTmp(Object[] args, Annotation[][] annotations) {
		if (ArrayUtils.isEmpty(args)) {
			return args;
		}
		Object[] argsTmp = new Object[args.length];
		for (int i = 0; i < args.length; i++) {
			/**
			 * 以下条件置空:
			 * Servlet相关对象
			 * 对象有UnSaveLogArgsAnnotation标识
			 * 参数声明有UnSaveLogArgsAnnotation标识
			 */
			if (args[i] != null
					&& (args[i] instanceof HttpServletRequest
							|| args[i] instanceof HttpServletResponse
							|| args[i].getClass().getName().startsWith(
									"javax.servlet")
							|| args[i].getClass().isAnnotationPresent(
									UnSaveLogArgsAnnotation.class) || hasUnSaveLogArgs(annotations[i]))) {
				// 如果是则置为空(临时以包名判断)
				argsTmp[i] = null;
			} else {
				argsTmp[i] = args[i];
			}
		}
		return argsTmp;
	}

	/**
	 * 当前参数是否有UnSaveLogArgsAnnotation标识(即是否不保存)
	 * 
	 * @param annotations
	 * @return
	 */
	private Boolean hasUnSaveLogArgs(Annotation[] annotations) {
		for (Annotation annotation : annotations) {
			if (annotation instanceof UnSaveLogArgsAnnotation) {
				return true;
			}
		}
		return false;
	}

	public void setApplicationContext(ApplicationContext context)
			throws BeansException {
		this.context = context;
	}

	public void setBeanContext(BeanContext beanContext) {
		this.beanContext = beanContext;
	}

	public void setIsMustWithUser(Boolean isMustWithUser) {
		this.isMustWithUser = isMustWithUser;
	}
}
